package com.atguigu.gmall.gateway.filter;


import com.alibaba.nacos.common.utils.StringUtils;
import com.atguigu.gmall.common.utils.CookieUtils;
import com.atguigu.gmall.common.utils.IpUtils;
import com.atguigu.gmall.common.utils.JwtUtils;
import com.atguigu.gmall.gateway.config.JwtProperties;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

@Component
public class AuthGatewayFilterFactory extends AbstractGatewayFilterFactory<AuthGatewayFilterFactory.Pathconfig> {
    @Autowired
    private JwtProperties jwtProperties;

    public AuthGatewayFilterFactory() {
        super(Pathconfig.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("paths");
    }

    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.GATHER_LIST;
    }
    @Data
    public static class Pathconfig{
        private List<String> paths;
    }

    @Override
    public GatewayFilter apply(Pathconfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest();
                ServerHttpResponse response = exchange.getResponse();

                //判断当前请求在不在拦截名单中，如果不在就直接放行
                String curpath = request.getURI().getPath();//获取当前请求的路径
                List<String> paths = config.paths;//拦截名单


                //拦截名单为null，表示拦截所有请求
                //如果拦截名单不为空，并且curpath当前请求路径中开头不包含拦截名单的内容，就放行
                if (!CollectionUtils.isEmpty(paths) && paths.stream().anyMatch(path->curpath.startsWith(path))){
                    return chain.filter(exchange);
                }


                //获取token，同步请求使用cookie，异步使用头信息
                String token = request.getHeaders().getFirst(jwtProperties.getToken());
                //如果从请求头中获取token为null
                if (StringUtils.isBlank(token)){
                    //就从请求头中获取cookie
                    MultiValueMap<String, HttpCookie> cookies = request.getCookies();
                     //如果token不为null，并且包含GMALL-TOKEN，才需要获取cookie
                    if (!CollectionUtils.isEmpty(cookies) && cookies.containsKey(jwtProperties.getCookieName())){
                        HttpCookie cookie = cookies.getFirst(jwtProperties.getCookieName());
                        token = cookie.getValue();//从cookie中获取token
                    }
                }


                //判断token是否为null，如果为null，说明没登录，则请求结束拦截重定向到登录页面
                if (StringUtils.isBlank(token)){
                    response.setStatusCode(HttpStatus.SEE_OTHER);//设置状态码
                    //重定向
                    response.getHeaders().set("Location","http://sso.gmall.com/toLogin.html?returnUrl="+request.getURI());
                    return response.setComplete();//终止请求处理，取消异步请求，立即返回响应
                }

                try {
                    //根据公钥解析token，如果出现异常，说明token不合法，则请求结束并重定向到登录页面
                    Map<String, Object> map = JwtUtils.getInfoFromToken(token, jwtProperties.getPublicKey());

                    //判断当前请求的ip地址和登录用户载荷中的ip是否一致，如果不一致说明token不属于当前用户，则请求结束并重定向到登录页面
                    String ip = IpUtils.getIpAddressAtGateway(request);//当前请求的ip
                    String curIp = map.get("ip").toString();//用户登录的ip
                    //如果两个ip不一致，就重定向到登录页面
                    if (!StringUtils.equals(ip,curIp)){
                        response.setStatusCode(HttpStatus.SEE_OTHER);//设置状态码
                        //重定向
                        response.getHeaders().set("Location","http://sso.gmall.com/toLogin.html?returnUrl="+request.getURI());
                        return response.setComplete();//终止请求处理，取消异步请求，立即返回响应
                    }


                    //把解析后的载荷内容传给后续服务
                    //创建一个header副本，并且设置参数
                    request.mutate().header("userId",map.get("userId").toString())
                            .header("username",map.get("username").toString()).build();
                    //替换当前request请求
                    exchange.mutate().request(request).build();


                    //放行
                    return chain.filter(exchange);
                } catch (Exception e) {
                    response.setStatusCode(HttpStatus.SEE_OTHER);//设置状态码
                    //重定向
                    response.getHeaders().set("Location","http://sso.gmall.com/toLogin.html?returnUrl="+request.getURI());
                    return response.setComplete();//终止请求处理，取消异步请求，立即返回响应
                }
            }
        };
    }


}
